#include "mtm_ex4.h"
#include "mtmtest.h"
#include "Store.h"
using namespace mtm;

namespace StoreTest {
/* #################### Auxiliary classes and functions ################### */

void makePrintStore(Store& store) {
	ASSERT_NO_THROW(store.addAlbum(3, 33, 1933, 333, "song1", "artist1"));
	ASSERT_NO_THROW(store.addAlbum(4, 44, 1944, 444, "song2", "artist2"));
	ASSERT_NO_THROW(store.addAlbum(2, 33, 1933, 333, "song3", "artist3"));
	ASSERT_NO_THROW(store.addAlbum(10, 44, 1944, 444, "song4", "artist4"));
	ASSERT_NO_THROW(store.addAlbum(12, 33, 1933, 333, "song5", "artist5"));
	ASSERT_NO_THROW(store.addAlbum(7, 44, 1944, 444, "song6", "artist6"));
	ASSERT_NO_THROW(store.addAlbum(70, 44, 1944, 2, "song7", "artist7"));

	store.addCustomer(96, "Brenda");
	store.addCustomer(35, "Bill");
	store.addCustomer(22, "Bob");
	store.addCustomer(67, "Bliss");

	store.createNewOrder(96, 5);
	store.addToOrder(5, 10);
	store.payOrder(5);
	store.createNewOrder(96, 6);
	store.addToOrder(6, 70);
	store.payOrder(6);
	store.createNewOrder(96, 8);
	store.addToOrder(8, 4);
}

/* ###################### Album,Order,Customer Tests ##################### */

void addAlbumTest() {
	Store newStore;
	// checking invalid parameters.
	ASSERT_THROWS(INVALID_BARCODE,
			newStore.addAlbum(0, 0, 5000, -10, "abc", "def"));
	ASSERT_THROWS(INVALID_PRICE,
			newStore.addAlbum(1, 0, 5000, -10, "abc", "def"));
	ASSERT_THROWS(INVALID_YEAR,
			newStore.addAlbum(1, 20, 5000, -10, "abc", "def"));
	ASSERT_THROWS(INVALID_NUM_PURCHASED,
			newStore.addAlbum(1, 20, 1986, -10, "abc", "def"));
	// adding a valid album.
	ASSERT_NO_THROW(newStore.addAlbum(50, 20, 1986, 10, "abc", "def"));
	// check if adding the same album throws exception
	ASSERT_THROWS(BARCODE_ALREADY_EXISTS,
			newStore.addAlbum(50, 47, 1945, 12, "ert", "bbc"));

	// checking that an inventory album can be used within orders.
	ASSERT_NO_THROW(newStore.addCustomer(1, "Bob"));
	ASSERT_NO_THROW(newStore.createNewOrder(1, 12));
	ASSERT_NO_THROW(newStore.addToOrder(12, 50));
}

void removeAlbumTest() {
	Store newStore;
	// checking invalid parameters.
	ASSERT_THROWS(INVALID_BARCODE, newStore.removeAlbum(-5));
	// insert a valid album.
	ASSERT_NO_THROW(newStore.addAlbum(50, 20, 1986, 10, "abc", "def"));
	// creating a new customer and a new order using the newly created album.
	ASSERT_NO_THROW(newStore.addCustomer(1, "Bob"));
	ASSERT_NO_THROW(newStore.createNewOrder(1, 12));
	ASSERT_NO_THROW(newStore.addToOrder(12, 50));
	// remove the album from the order and the inventory.
	ASSERT_NO_THROW(newStore.removeFromOrder(12, 50));
	ASSERT_NO_THROW(newStore.removeAlbum(50));
	// confirm album was removed from inventory by trying to use it.
	ASSERT_THROWS(ALBUM_DOESNT_EXIST, newStore.addToOrder(12, 50));
}

void addCustomerTest() {
	Store newStore;
	ASSERT_NO_THROW(newStore.addCustomer(1, "Bob"));
	// check invalid parameters.
	ASSERT_THROWS(INVALID_ID, newStore.addCustomer(0, "Bob"));
	// check if customer is already listed.
	ASSERT_THROWS(CUSTOMER_ALREADY_EXISTS, newStore.addCustomer(1, "John"));
	// add an order to the newly created customer.
	ASSERT_NO_THROW(newStore.createNewOrder(1, 12));
}

void createNewOrderTest() {
	Store store;
	store.addAlbum(1, 1, 2000, 1, "", "");
	store.addCustomer(1, "bob");
	store.addCustomer(100, "dude");
	// check invalid parameters
	ASSERT_THROWS(INVALID_ID, store.createNewOrder(0, 1));
	ASSERT_THROWS(INVALID_ID, store.createNewOrder(-1, 1));
	ASSERT_THROWS(INVALID_ORDER_NUMBER, store.createNewOrder(1, 0));
	ASSERT_THROWS(INVALID_ORDER_NUMBER, store.createNewOrder(1, -1));
	ASSERT_THROWS(CUSTOMER_DOESNT_EXIST, store.createNewOrder(2, 1));
	// create order 10 for customer 1
	ASSERT_NO_THROW(store.createNewOrder(1, 10));
	ASSERT_NO_THROW(store.addToOrder(10, 1));
	// try to create a new order for customer 1
	ASSERT_THROWS(CUSTOMER_HAS_OPEN_ORDER, store.createNewOrder(1, 20));
	// create a new order for another customer
	// with existing order number
	ASSERT_THROWS(ORDER_ALREADY_EXISTS, store.createNewOrder(100, 10));
	// with new order number
	ASSERT_NO_THROW(store.createNewOrder(100, 30));
}

void addToOrderTest() {
	// add some valid albums to a store.
	Store newStore;
	newStore.addAlbum(50, 20, 1986, 234, "Best Jazz vol2", "Various");
	newStore.addAlbum(54, 20, 1986, 75, "House of cards", "the match sticks");
	newStore.addAlbum(67, 20, 1986, 132, "Ralfy's got an axe", "Face swap");
	// add a new customer and an order for that customer.
	newStore.addCustomer(1, "Bob");
	newStore.createNewOrder(1, 12);
	// check invalid parameters.
	ASSERT_THROWS(INVALID_ORDER_NUMBER, newStore.addToOrder(-12, -1));
	ASSERT_THROWS(INVALID_BARCODE, newStore.addToOrder(11, 0));
	// check for missing album and missing order.
	ASSERT_THROWS(ALBUM_DOESNT_EXIST, newStore.addToOrder(11, 52));
	ASSERT_THROWS(ORDER_DOESNT_EXIST, newStore.addToOrder(11, 54));
	// add a valid album to the valid order and pay the order.
	ASSERT_NO_THROW(newStore.addToOrder(12, 50));
	ASSERT_NO_THROW(newStore.payOrder(12));
	// confirm that albums cannot be added to an already paid order.
	ASSERT_THROWS(ORDER_WAS_ALREADY_PAID, newStore.addToOrder(12, 67));
}

void removeFromOrderTest() {
	Store store;
	store.addCustomer(1, "Bob");
	store.addAlbum(10, 1, 1, 1, "", "");
	store.addAlbum(20, 1, 1, 1, "", "");
	store.addAlbum(30, 1, 1, 1, "", "");
	store.createNewOrder(1, 22);
	store.addToOrder(22, 10);

	// check invalid parameters
	ASSERT_THROWS(INVALID_ORDER_NUMBER, store.removeFromOrder(-1, -1));
	ASSERT_THROWS(INVALID_ORDER_NUMBER, store.removeFromOrder(0, 0));
	ASSERT_THROWS(INVALID_BARCODE, store.removeFromOrder(1, -1));
	ASSERT_THROWS(INVALID_BARCODE, store.removeFromOrder(1, 0));
	// try to remove order that doesn't exist
	ASSERT_THROWS(ORDER_DOESNT_EXIST, store.removeFromOrder(1, 1));
	store.payOrder(22);
	// try to remove order that was already paid
	ASSERT_THROWS(ORDER_WAS_ALREADY_PAID, store.removeFromOrder(22, 10));
	store.createNewOrder(1, 23);
	// try to remove from empty order
	ASSERT_THROWS(ORDER_DOESNT_HAVE_ALBUM, store.removeFromOrder(23, 10));
	// successfully remove albums from order
	store.addToOrder(23, 10);
	store.addToOrder(23, 30);
	ASSERT_NO_THROW(store.removeFromOrder(23, 10));
	store.addToOrder(23, 20);
	ASSERT_NO_THROW(store.removeFromOrder(23, 20));
	ASSERT_NO_THROW(store.removeFromOrder(23, 30));
	// verify all albums were removed from order
	ASSERT_THROWS(ORDER_IS_EMPTY, store.payOrder(23));
}

void payOrderTest() {
	Store store;
	store.addCustomer(1, "Bob");
	store.addAlbum(10, 1, 1, 1, "", "");
	store.addAlbum(20, 1, 1, 2, "", "");
	store.addAlbum(30, 1, 1, 3, "", "");
	store.createNewOrder(1, 22);

	// check invalid parameters
	ASSERT_THROWS(INVALID_ORDER_NUMBER, store.payOrder(-1));
	ASSERT_THROWS(INVALID_ORDER_NUMBER, store.payOrder(0));

	// try to pay for order that doesn't exist
	ASSERT_THROWS(ORDER_DOESNT_EXIST, store.payOrder(100));

	// try to pay for empty order
	ASSERT_THROWS(ORDER_IS_EMPTY, store.payOrder(22));

	// successfully pay for order
	store.addToOrder(22, 10);
	store.addToOrder(22, 30);
	ASSERT_NO_THROW(store.payOrder(22));

	// try to pay for order that was already paid
	ASSERT_THROWS(ORDER_WAS_ALREADY_PAID, store.payOrder(22));

	// try to pay for order with missing albums
	store.createNewOrder(1, 23);
	store.addToOrder(23, 10);
	store.addToOrder(23, 20);
	store.addToOrder(23, 30);
	store.removeAlbum(20);
	ASSERT_THROWS(ALBUM_MISSING, store.payOrder(23));
}

/* #################### getRecommendationsTest ###################### */

void getRecommendationsSuccess() {

	Store store;
	makePrintStore(store);

	ASSERT_EQUALS(
			getRecommendationsHeaderString(7)
					+ getAlbumFullString(10, 44, 1944, 445, "song4", "artist4")
					+ getAlbumFullString(4, 44, 1944, 444, "song2", "artist2")
					+ getAlbumFullString(7, 44, 1944, 444, "song6", "artist6")
					+ getAlbumFullString(2, 33, 1933, 333, "song3", "artist3")
					+ getAlbumFullString(3, 33, 1933, 333, "song1", "artist1")
					+ getAlbumFullString(12, 33, 1933, 333, "song5", "artist5")
					+ getAlbumFullString(70, 44, 1944, 3, "song7", "artist7"),
			store.getRecommendations(7));
}

void getRecommendationsFailure() {
	Store store;
	makePrintStore(store);

	ASSERT_NOT_EQUAL(
			getRecommendationsHeaderString(3)
					+ getAlbumFullString(3, 33, 1933, 333, "song3", "artist3")
					+ getAlbumFullString(4, 44, 1944, 444, "song4", "artist4")
					+ getAlbumFullString(2, 33, 1933, 111, "song3", "artist3"),
			store.getRecommendations(3));
}

void getRecommendationsExceptions() {
	Store store;
	makePrintStore(store);

	ASSERT_THROWS(INVALID_NUMBER_OF_RECOMMENDATIONS,
			store.getRecommendations(-3));
	ASSERT_THROWS(NOT_ENOUGH_RECOMMENDATIONS, store.getRecommendations(8));
}

void getRecommendationsTest() {
	getRecommendationsSuccess();
	getRecommendationsFailure();
	getRecommendationsExceptions();
}

/* ################### getInventoryReportTest ######################## */

void getInventoryReportSuccessWithUnpaidOrders() {
	// create a store and add to inventory.
	Store store;
	makePrintStore(store);
	// create a customer with an order, add albums to the order but don't pay
	store.addCustomer(1, "Bob");
	store.createNewOrder(1, 12);
	store.addToOrder(12, 10);
	store.addToOrder(12, 10);
	store.addToOrder(12, 70);
	// assert that unpaid orders do not influence output string.
	// and output is sorted by barcode.
	ASSERT_EQUALS(
			getAlbumsHeaderString(7)
					+ getAlbumFullString(2, 33, 1933, 333, "song3", "artist3")
					+ getAlbumFullString(3, 33, 1933, 333, "song1", "artist1")
					+ getAlbumFullString(4, 44, 1944, 444, "song2", "artist2")
					+ getAlbumFullString(7, 44, 1944, 444, "song6", "artist6")
					+ getAlbumFullString(10, 44, 1944, 445, "song4", "artist4")
					+ getAlbumFullString(12, 33, 1933, 333, "song5", "artist5")
					+ getAlbumFullString(70, 44, 1944, 3, "song7", "artist7"),
			store.getInventoryReport());
}

void getInventoryReportSuccessWithPaidOrders() {
	// create a store and add to inventory.
	Store store;
	makePrintStore(store);

	store.addCustomer(1, "Bob");
	store.createNewOrder(1, 12);
	store.addToOrder(12, 10);
	store.addToOrder(12, 10);
	store.addToOrder(12, 70);

	store.payOrder(12);

	ASSERT_EQUALS(
			getAlbumsHeaderString(7)
					+ getAlbumFullString(2, 33, 1933, 333, "song3", "artist3")
					+ getAlbumFullString(3, 33, 1933, 333, "song1", "artist1")
					+ getAlbumFullString(4, 44, 1944, 444, "song2", "artist2")
					+ getAlbumFullString(7, 44, 1944, 444, "song6", "artist6")
					+ getAlbumFullString(10, 44, 1944, 447, "song4", "artist4")
					+ getAlbumFullString(12, 33, 1933, 333, "song5", "artist5")
					+ getAlbumFullString(70, 44, 1944, 4, "song7", "artist7"),
			store.getInventoryReport());
}

void getInventoryReportExceptions() {
	Store store;
	ASSERT_THROWS(EMPTY_INVENTORY, store.getInventoryReport());
}

void getInventoryReportFailure() {
	Store store;
	ASSERT_NO_THROW(store.addAlbum(3, 33, 1933, 333, "song1", "artist1"));
	ASSERT_NO_THROW(store.addAlbum(4, 44, 1944, 444, "song2", "artist2"));

	ASSERT_NOT_EQUAL(
			getAlbumsHeaderString(2)
					+ getAlbumFullString(4, 44, 1944, 444, "song2", "artist2")
					+ getAlbumFullString(3, 33, 1933, 333, "song1", "artist1"),
			store.getInventoryReport());
}

void getInventoryReportTest() {
	getInventoryReportSuccessWithUnpaidOrders();
	getInventoryReportSuccessWithPaidOrders();
	getInventoryReportExceptions();
	getInventoryReportFailure();
}

/* ################### getCustomersReportTest ######################## */
void getCustomersReportExceptions() {
	Store store;
	ASSERT_THROWS(NO_CUSTOMERS, store.getCustomersReport());
}
void getCustomersReportSuccess() {
	Store store;
	makePrintStore(store);

	ASSERT_EQUALS(
			getCustomersHeaderString(4)
					+ getCustomerDescriptionString(22, "Bob")
					+ getCustomerDescriptionString(35, "Bill")
					+ getCustomerDescriptionString(67, "Bliss")
					+ getCustomerDescriptionString(96, "Brenda"),
			store.getCustomersReport());
}

void getCustomersReportTest() {
	getCustomersReportExceptions();
	getCustomersReportSuccess();
}

/* ################### getCustomerReportTest ######################## */

void getCustomerReportExceptions() {
	Store store;
	makePrintStore(store);

	ASSERT_THROWS(INVALID_ID, store.getCustomerReport(0));
	ASSERT_NO_THROW(store.getCustomerReport(22));
	ASSERT_THROWS(CUSTOMER_DOESNT_EXIST, store.getCustomerReport(100));
}
void getCustomerReportSuccess() {
	Store store;
	makePrintStore(store);

	ASSERT_EQUALS(
			getCustomerHeaderString(96, "Brenda", 2)
					+ getOrderHeaderString(5, 96, 1, true)
					+ getAlbumMinimalString("song4", "artist4", 1944, 44)
					+ getOrderHeaderString(6, 96, 1, true)
					+ getAlbumMinimalString("song7", "artist7", 1944, 44)
					+ getOrderHeaderString(8, 96, 1, false)
					+ getAlbumMinimalString("song2", "artist2", 1944, 44),
			store.getCustomerReport(96));
}

void getCustomerReportTest() {
	getCustomersReportExceptions();
	getCustomerReportSuccess();
}

/* ################### getRevenueReportTest ######################## */

void getRevenueReportTest() {
	Store store;
	ASSERT_EQUALS(getRevenueHeaderString(0,0,0), store.getRevenueReport());

	makePrintStore(store);
	store.createNewOrder(35, 60);
	store.addToOrder(60, 70);
	store.payOrder(60);

	ASSERT_EQUALS(
			getRevenueHeaderString(4, 3, 132)
					+ getRevenueFromCustomerString(22, "Bob", 0, 0)
					+ getRevenueFromCustomerString(35, "Bill", 1, 44)
					+ getRevenueFromCustomerString(67, "Bliss", 0, 0)
					+ getRevenueFromCustomerString(96, "Brenda", 2, 88),
			store.getRevenueReport());
}

}

/* uncomment to run tests, or copy the relevant code */
//using namespace StoreTest;
//int main() {
//	RUN_TEST(addAlbumTest);
//	RUN_TEST(removeAlbumTest);
//	RUN_TEST(addCustomerTest);
//	RUN_TEST(createNewOrderTest);
//	RUN_TEST(addToOrderTest);
//	RUN_TEST(removeFromOrderTest);
//	RUN_TEST(payOrderTest);
//	RUN_TEST(getRecommendationsTest);
//	RUN_TEST(getInventoryReportTest);
//	RUN_TEST(getCustomersReportTest);
//	RUN_TEST(getCustomerReportTest);
//	RUN_TEST(getRevenueReportTest);
//	return 0;
//}

